package cmm;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.TreeMap;

import clusterMessages.ClusterMessage_TrainingTerminateTask;
import clusterMessages.ClusterMessage_TrainingUpdateTask;
import clusterMessages.TaskTrainingData;


public class Mentor implements IMentor {

	private Thread MentorThread;
	
	private Integer TrainingTaskID;
	
	private String TraineeName;
	
	private TreeMap<Integer, TaskEntry> TaskDB;
	
	private ClusterManagementModule CMM;
	
	private boolean InteractiveTrainingMode;

//  -----------------------------------------------------------------
//  Private Methods	
//  -----------------------------------------------------------------
	
	private void WaitForEnter(String MessageToUser) {

		System.out.println(MessageToUser);
		BufferedReader userIn =
	        new BufferedReader(
	        new InputStreamReader( System.in ) );
		
		try {
			userIn.readLine();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
	}
}
	
//  -----------------------------------------------------------------
//  Public Methods	
//  -----------------------------------------------------------------	
	
	/**
	 * This creates a new Mentor. The Mentor runs in a new thread, 
	 * which is created in the constructor. The Mentor sends training
	 * messages to the Trainee machine. Each message transferes a single
	 * TaskEntry in the Snapshot Task database.
	 * 
	 * @param CMM - A reference to the CMM object.
	 * @param TrainingTaskID - The TaskID of the training session.
	 * @param TraineeMachineName - The machine name of the Trainee.
	 * @param CMMTaskDB - The snapshot Task database.
	 */
	public Mentor(ClusterManagementModule CMM, Integer TrainingTaskID, String TraineeMachineName, TreeMap<Integer, TaskEntry> CMMTaskDB, boolean InteractiveTrainingMode) {
		
		// Set training parameters
		this.CMM = CMM;
		this.TrainingTaskID = TrainingTaskID;
		TraineeName = TraineeMachineName;
		TaskDB = CMMTaskDB;
		this.InteractiveTrainingMode = InteractiveTrainingMode;
		
		CMM.AddLogEntry("Mentor", "Info - A new Mentor initialized for Trainee \"" + TraineeMachineName + "\" with training session ID " + TrainingTaskID);
		
		// Start the Mentor thread
		MentorThread = new Thread(this);
		MentorThread.start();
	}

//  -----------------------------------------------------------------	
	
	public void run() {
		
		CMM.AddLogEntry("Mentor", "Info - Started a new Mentor thread.");
		CMM.AddLogEntry("Mentor", "Info - Begining to send Task entries from the snapshot Task database to the Cluster.");
		
		while(!TaskDB.isEmpty()){
			// Get the task with the minimum TaskID from the DB
			Integer MinTaskID = TaskDB.firstKey();
			TaskEntry Entry = TaskDB.get(MinTaskID);
			
			// Put the Task entry into a data package to be sent to the Cluster
			TaskTrainingData Data = new TaskTrainingData();
			Data.setTraineeName(TraineeName);
			Data.setTrainedTaskID(MinTaskID);
			Data.setTrainedTaskEntry(Entry);
			// Create a message to be sent to the cluster with this task
			ClusterMessage_TrainingUpdateTask msg = new ClusterMessage_TrainingUpdateTask();
			msg.setTaskID(TrainingTaskID);
			msg.setData(Data);
			
			// If going to send this Training session as a "Training Update Message"
			if(MinTaskID.equals(TrainingTaskID)){
				// remove it from the Mentor Task database and skip it
				TaskDB.remove(MinTaskID);
				continue;
				// This happens if a Training session Migrates between Machines.
			}
			
			if(InteractiveTrainingMode)
				WaitForEnter("Press Enter to send \"Training Update Message\" of TaskID=" + MinTaskID + " to Machine " + TraineeName);
			
			CMM.AddLogEntry("Mentor", "Info - Sending training message to Trainee \"" + TraineeName + "\" of TaskID " + MinTaskID);
			try {
				// Send the message to the Cluster
				CMM.SendTrainingMsg(msg);
			} catch (CMMExn_ClusterErr e) {
				// TODO remove print stack
				e.printStackTrace();
				CMM.AddLogEntry("Mentor", "Error - Failed to send the training message to the cluster.");
				return;
			}
			
			// remove the Task entry from the database
			TaskDB.remove(MinTaskID);
		}
		
		// Finished mentoring the Trainee
		CMM.AddLogEntry("Mentor", "Info - Finished tranfering the Task database to the Trainee \"" + TraineeName + "\"");
		
		// Create a training session terminating message
		ClusterMessage_TrainingTerminateTask msg = new ClusterMessage_TrainingTerminateTask();
		msg.setTaskID(TrainingTaskID);
		msg.setTraineeName(TraineeName);
		
		CMM.AddLogEntry("Mentor", "Info - Sending training session termination message to Trainee \"" + TraineeName + "\". Session TaskID is " + TrainingTaskID);
		try {
			CMM.SendTrainingMsg(msg);
		} catch (CMMExn_ClusterErr e) {
			// TODO remove print stack
			e.printStackTrace();
			CMM.AddLogEntry("Mentor", "Error - Failed to send the training message to the cluster.");
		}
	}
	
//  -----------------------------------------------------------------	
	
	public void Close() {

		// (in milliseconds)		
		final long KillWaitTime = 1000;
		
		MentorThread.interrupt();
		try {
			MentorThread.join(KillWaitTime);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}
